import json

from django.http import HttpResponse
from utils.resFormatUtil import ResFormat
from django.views import View
from django.utils.decorators import method_decorator
from utils.verifyUtil import verifyParam, ImageVerify
from celery_tasks.email.tasks import send_mail_task
from django_redis import get_redis_connection
from apps.user.models import User, UserDetail
from django.contrib.auth.hashers import make_password, check_password
from django.db.utils import IntegrityError
from datetime import datetime, timedelta
from config import token_conf
from django.core import serializers
from fdfs_client.client import Fdfs_client, get_tracker_conf
from yige_shopping.settings import FASTDFS_SERVER_DOMAIN

import re, random, io, jwt, json, logging

EMAIL_KEY_TEMPLATE = 'email:verification:%s'
LOGIN_KEY_TEMPLATE = 'login:verification:%s'

# 导入日志器
logger = logging.getLogger(__name__)

# tracker server
tracker_path = get_tracker_conf('utils/fastdfs/client.conf')
client = Fdfs_client(tracker_path)


# Create your views here.

# 发送邮件验证码
class EmailVerificationView(View):
    DURATION = 5

    @method_decorator(verifyParam('email'))
    def post(self, request):
        post = request.POST
        email = post.get('email')

        # 验证邮箱的格式
        re_email = r'(\w+)@(\w+)\.(\w+)'
        if not re.match(re_email, email):
            return ResFormat(status='PARAM_ERROR').json_response()

        cache_default = get_redis_connection()
        if cache_default.get(email):
            return ResFormat('ACCESS_RESTRICTION').json_response()

        verification_code = random.randint(100000, 999999)  # 六位数的随机验证码

        # self.send_mail_task(email, verification_code, self.DURATION)

        # 提交一个异步任务放入队列中
        send_mail_task.delay(email, verification_code, self.DURATION)  # 调用delay方法，使用celery发送邮箱验证码
        return ResFormat().json_response()


# 注册
class RegisterView(View):
    @method_decorator(verifyParam('email', 'password', 'verify'))
    def post(self, request):
        post = request.POST
        email = post.get('email')
        password = make_password(post.get('password'), None, 'pbkdf2_sha1')
        verify = post.get('verify')

        # 验证邮箱的格式
        re_email = r'(\w+)@(\w+)\.(\w+)'
        if not re.match(re_email, email):
            return ResFormat(status='PARAM_ERROR').json_response()

        # 验证码校验：获取redis数据库中该邮箱的验证码，与用户输入的验证码比较
        cache = get_redis_connection(alias='verify_codes')
        redis_verify = cache.get(EMAIL_KEY_TEMPLATE % email)
        if not redis_verify or verify != redis_verify:
            return ResFormat(status='PARAM_ERROR').json_response()

        cache.delete(EMAIL_KEY_TEMPLATE % email)  # 释放验证码存储空间，及时清理

        try:
            User.objects.create(email=email, password=password)
        except IntegrityError:
            return ResFormat('ACCOUNT_REGISTERED').json_response()
        except Exception:
            return ResFormat('UNKNOWN_ERROR').json_response()
        else:
            return ResFormat().json_response()


# 图片验证码
class ImageVerifyView(View):
    EXPIRE_TIME = 60 * 3

    def get(self, request, uuid):
        imageVerify = ImageVerify()
        img, code = imageVerify.veri_code()  # 得到图片验证码（响应给用户），答案（存起来）

        # 创建一个字节流通道，相当于定义了一个盒子，字节流数据的盒子
        img_bytes = io.BytesIO()
        # 将图片数据存入字节流通道
        img.save(img_bytes, format='PNG')
        # 从字节流管道中获取二进制
        image_bytes = img_bytes.getvalue()

        # 将uuid对应验证码答案缓存到redis中
        cache = get_redis_connection(alias='verify_codes')
        cache.set(LOGIN_KEY_TEMPLATE % uuid, code, self.EXPIRE_TIME)

        return HttpResponse(image_bytes, content_type='image/png')


# 登陆
class LoginView(View):
    EXPIRATION_TIME = 1

    @method_decorator(verifyParam('email', 'password', 'verify'))
    def post(self, request, uuid):
        post = request.POST
        email = post.get('email')
        password = post.get('password')
        verify = post.get('verify')

        # 验证码校验，不区分大小
        cache = get_redis_connection(alias='verify_codes')
        redis_verify = cache.get(LOGIN_KEY_TEMPLATE % uuid)
        if not redis_verify:
            return ResFormat(status='OVERDUE_ABNORMAL').json_response()

        if redis_verify.upper() != verify.upper():
            return ResFormat(status='PARAM_ERROR').json_response()

        cache.delete(LOGIN_KEY_TEMPLATE % uuid)

        user_list = User.objects.filter(email=email)
        if not user_list:
            return ResFormat(status='ACCESS_RESTRICTION').json_response()

        db_password = check_password(password, user_list[0].password)
        if not db_password or user_list[0].state != 0:
            # 密码不正确或者账号状态异常，返回访问限制
            return ResFormat(status='ACCESS_RESTRICTION').json_response()

        # 已经登陆成功，发放身份令牌
        payload = {
            'iss': 'LoginView_post',
            'sub': 'login',
            'exp': datetime.now() + timedelta(days=self.EXPIRATION_TIME),  # 指定什么时候到期
            'iat': int(datetime.now().timestamp()),
            'data': email
        }

        # 解析令牌可能报错
        user_token = jwt.encode(payload, token_conf.TOKEN_SECRET_KEY, token_conf.ALGORITHMS, token_conf.HEADERS)

        return ResFormat(data=user_token).json_response()


# 用户信息
class UserInfoView(View):
    def get(self, request):
        # 请求经过中间件
        user = User.objects.filter(email=request.email)[0]

        data = {
            'email': user.email
        }
        # 判断用户是否有详情数据
        if hasattr(user, 'userdetail'):
            # 如果有就获取user.userdetail，因为返回的是一个model对象，转为json，序列化（只能是列表）。使用json.loads将字符串转为python列表
            detail = json.loads(serializers.serialize('json', [user.userdetail], ensure_ascii=False))  # 这个方法只能序列化列表
            # 对不需要，铭感数据进行处理
            detail[0]['fields'].pop('user')
            data['detail'] = detail[0]['fields']
        return ResFormat(data=data).json_response()

    def post(self, request):
        post = request.POST
        nickname = post.get('nickname')
        phone = post.get('phone')
        age = post.get('age')
        sex = post.get('sex')
        address = post.get('address')
        birthday = post.get('birthday')

        print(nickname, phone, age, sex, address, birthday)

        # 获取用户
        user = User.objects.filter(email=request.email)[0]
        if hasattr(user, 'userdetail'):
            # 如果该用户有详情数据，则得到详情模型类，save做修改
            user_detail = user.userdetail
        else:
            # 如果该用户没有详情数据，创建一个详情模型类，save做创建
            user_detail = UserDetail()

        user_detail.nickname = nickname
        user_detail.phone = phone
        user_detail.age = age if age else None  # NULL
        user_detail.sex = sex
        user_detail.address = address
        user_detail.birthday = birthday if birthday else None  # NULL
        try:
            user_detail.save()
        except Exception as e:
            logger.error(f'用户信息保存错误：{e}')
            return ResFormat('PARAM_ERROR').json_response()

        return ResFormat().json_response()


# 头像
class PortraitView(View):
    def post(self, request):
        # 获取请求中的文件数据
        files = request.FILES.get('file')

        # 校验
        if not files or files.content_type not in ('image/jpeg', 'image/png', 'image/gif'):
            return ResFormat(status='PARAM_ERROR').json_response()

        # 获取图片类型，后缀名
        try:
            image_ext_name = files.name.split('.')[-1]
        except Exception as e:
            logger.error(f'图片后缀名错误：{e}')
            image_ext_name = 'png'

        # FastDFS
        try:
            upload_res = client.upload_by_buffer(files.read(), file_ext_name=image_ext_name)
        except Exception as e:
            logger.error(f'图片上传出现异常：{e}')
            return ResFormat(status='UNKNOWN_ERROR').json_response()
        else:
            if upload_res.get('Status') != 'Upload successed.':
                return ResFormat(status='UPLOAD_FAIL').json_response()
            else:
                image_name = upload_res.get('Remote file_id').decode()
                image_url = FASTDFS_SERVER_DOMAIN + image_name

                user = User.objects.filter(email=request.email)[0]
                if hasattr(user, 'userdetail'):
                    user_detail = user.userdetail
                    user_detail.portrait = image_url
                    user_detail.save()
                else:
                    UserDetail.objects.create(portrait=image_url, user_id=user.id)

                return ResFormat(data=image_url).json_response()
